package com.kexilo.core.common.security;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.regex.Pattern;

/**
 * 敏感数据加密组件
 * 提供敏感数据的加密、解密和脱敏功能
 * 
 * @author Kexilo
 */
@Component
public class SensitiveDataEncryption {
    
    private static final Logger log = LoggerFactory.getLogger(SensitiveDataEncryption.class);
    
    // AES-GCM加密算法
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/GCM/NoPadding";
    private static final int GCM_IV_LENGTH = 12;
    private static final int GCM_TAG_LENGTH = 16;
    
    // 默认加密密钥（生产环境应从配置文件或密钥管理系统获取）
    @Value("${kexilo.security.encryption.key:kexilo-default-secret-key-32bit}")
    private String encryptionKey;
    
    // 敏感数据匹配模式
    private static final Pattern PHONE_PATTERN = Pattern.compile("1[3-9]\\d{9}");
    private static final Pattern EMAIL_PATTERN = Pattern.compile("[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}");
    private static final Pattern ID_CARD_PATTERN = Pattern.compile("\\d{17}[\\dXx]");
    private static final Pattern BANK_CARD_PATTERN = Pattern.compile("\\d{16,19}");
    
    /**
     * 加密敏感数据
     * 
     * @param plainText 明文数据
     * @return 加密后的数据（Base64编码）
     */
    public String encrypt(String plainText) {
        if (plainText == null || plainText.isEmpty()) {
            return plainText;
        }
        
        try {
            SecretKey secretKey = getSecretKey();
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            
            // 生成随机IV
            byte[] iv = new byte[GCM_IV_LENGTH];
            new SecureRandom().nextBytes(iv);
            GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
            
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
            byte[] encryptedData = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
            
            // 将IV和加密数据合并
            byte[] encryptedWithIv = new byte[GCM_IV_LENGTH + encryptedData.length];
            System.arraycopy(iv, 0, encryptedWithIv, 0, GCM_IV_LENGTH);
            System.arraycopy(encryptedData, 0, encryptedWithIv, GCM_IV_LENGTH, encryptedData.length);
            
            String result = Base64.getEncoder().encodeToString(encryptedWithIv);
            log.debug("数据加密成功 - 原文长度: {}, 密文长度: {}", plainText.length(), result.length());
            return result;
        } catch (Exception e) {
            log.error("数据加密失败", e);
            throw new RuntimeException("数据加密失败", e);
        }
    }
    
    /**
     * 解密敏感数据
     * 
     * @param encryptedText 加密的数据（Base64编码）
     * @return 解密后的明文数据
     */
    public String decrypt(String encryptedText) {
        if (encryptedText == null || encryptedText.isEmpty()) {
            return encryptedText;
        }
        
        try {
            byte[] encryptedWithIv = Base64.getDecoder().decode(encryptedText);
            
            // 分离IV和加密数据
            byte[] iv = new byte[GCM_IV_LENGTH];
            byte[] encryptedData = new byte[encryptedWithIv.length - GCM_IV_LENGTH];
            System.arraycopy(encryptedWithIv, 0, iv, 0, GCM_IV_LENGTH);
            System.arraycopy(encryptedWithIv, GCM_IV_LENGTH, encryptedData, 0, encryptedData.length);
            
            SecretKey secretKey = getSecretKey();
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
            
            cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
            byte[] decryptedData = cipher.doFinal(encryptedData);
            
            String result = new String(decryptedData, StandardCharsets.UTF_8);
            log.debug("数据解密成功 - 密文长度: {}, 明文长度: {}", encryptedText.length(), result.length());
            return result;
        } catch (Exception e) {
            log.error("数据解密失败", e);
            throw new RuntimeException("数据解密失败", e);
        }
    }
    
    /**
     * 脱敏手机号
     * 
     * @param phone 手机号
     * @return 脱敏后的手机号
     */
    public String maskPhone(String phone) {
        if (phone == null || !PHONE_PATTERN.matcher(phone).matches()) {
            return phone;
        }
        return phone.substring(0, 3) + "****" + phone.substring(7);
    }
    
    /**
     * 脱敏邮箱
     * 
     * @param email 邮箱地址
     * @return 脱敏后的邮箱
     */
    public String maskEmail(String email) {
        if (email == null || !EMAIL_PATTERN.matcher(email).matches()) {
            return email;
        }
        
        int atIndex = email.indexOf('@');
        if (atIndex <= 1) {
            return email;
        }
        
        String username = email.substring(0, atIndex);
        String domain = email.substring(atIndex);
        
        if (username.length() <= 2) {
            return email;
        }
        
        String maskedUsername = username.charAt(0) + "***" + username.charAt(username.length() - 1);
        return maskedUsername + domain;
    }
    
    /**
     * 脱敏身份证号
     * 
     * @param idCard 身份证号
     * @return 脱敏后的身份证号
     */
    public String maskIdCard(String idCard) {
        if (idCard == null || !ID_CARD_PATTERN.matcher(idCard).matches()) {
            return idCard;
        }
        return idCard.substring(0, 6) + "********" + idCard.substring(14);
    }
    
    /**
     * 脱敏银行卡号
     * 
     * @param bankCard 银行卡号
     * @return 脱敏后的银行卡号
     */
    public String maskBankCard(String bankCard) {
        if (bankCard == null || !BANK_CARD_PATTERN.matcher(bankCard).matches()) {
            return bankCard;
        }
        
        if (bankCard.length() < 8) {
            return bankCard;
        }
        
        return bankCard.substring(0, 4) + "****" + "****" + bankCard.substring(bankCard.length() - 4);
    }
    
    /**
     * 脱敏姓名
     * 
     * @param name 姓名
     * @return 脱敏后的姓名
     */
    public String maskName(String name) {
        if (name == null || name.length() <= 1) {
            return name;
        }
        
        if (name.length() == 2) {
            return name.charAt(0) + "*";
        }
        
        StringBuilder masked = new StringBuilder();
        masked.append(name.charAt(0));
        for (int i = 1; i < name.length() - 1; i++) {
            masked.append("*");
        }
        masked.append(name.charAt(name.length() - 1));
        
        return masked.toString();
    }
    
    /**
     * 通用脱敏方法
     * 
     * @param data 原始数据
     * @param type 脱敏类型
     * @return 脱敏后的数据
     */
    public String mask(String data, SensitiveType type) {
        if (data == null) {
            return null;
        }
        
        switch (type) {
            case PHONE:
                return maskPhone(data);
            case EMAIL:
                return maskEmail(data);
            case ID_CARD:
                return maskIdCard(data);
            case BANK_CARD:
                return maskBankCard(data);
            case NAME:
                return maskName(data);
            case ADDRESS:
                return maskAddress(data);
            default:
                return data;
        }
    }
    
    /**
     * 脱敏地址
     * 
     * @param address 地址
     * @return 脱敏后的地址
     */
    public String maskAddress(String address) {
        if (address == null || address.length() <= 6) {
            return address;
        }
        
        // 保留前6个字符（通常是省市区），其余用*号代替
        return address.substring(0, 6) + "****";
    }
    
    /**
     * 批量加密
     * 
     * @param dataArray 数据数组
     * @return 加密后的数据数组
     */
    public String[] batchEncrypt(String[] dataArray) {
        if (dataArray == null) {
            return null;
        }
        
        String[] encryptedArray = new String[dataArray.length];
        for (int i = 0; i < dataArray.length; i++) {
            encryptedArray[i] = encrypt(dataArray[i]);
        }
        
        return encryptedArray;
    }
    
    /**
     * 批量解密
     * 
     * @param encryptedArray 加密的数据数组
     * @return 解密后的数据数组
     */
    public String[] batchDecrypt(String[] encryptedArray) {
        if (encryptedArray == null) {
            return null;
        }
        
        String[] decryptedArray = new String[encryptedArray.length];
        for (int i = 0; i < encryptedArray.length; i++) {
            decryptedArray[i] = decrypt(encryptedArray[i]);
        }
        
        return decryptedArray;
    }
    
    /**
     * 获取密钥
     */
    private SecretKey getSecretKey() {
        // 确保密钥长度为32字节（256位）
        byte[] keyBytes = encryptionKey.getBytes(StandardCharsets.UTF_8);
        if (keyBytes.length != 32) {
            // 如果密钥长度不足32字节，进行填充或截断
            byte[] adjustedKey = new byte[32];
            if (keyBytes.length < 32) {
                System.arraycopy(keyBytes, 0, adjustedKey, 0, keyBytes.length);
                // 用0填充剩余部分
                for (int i = keyBytes.length; i < 32; i++) {
                    adjustedKey[i] = 0;
                }
            } else {
                System.arraycopy(keyBytes, 0, adjustedKey, 0, 32);
            }
            keyBytes = adjustedKey;
        }
        
        return new SecretKeySpec(keyBytes, ALGORITHM);
    }
    
    /**
     * 生成新的加密密钥
     * 
     * @return Base64编码的密钥
     */
    public static String generateNewKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
            keyGenerator.init(256);
            SecretKey secretKey = keyGenerator.generateKey();
            return Base64.getEncoder().encodeToString(secretKey.getEncoded());
        } catch (Exception e) {
            throw new RuntimeException("生成加密密钥失败", e);
        }
    }
    
    /**
     * 敏感数据类型枚举
     */
    public enum SensitiveType {
        PHONE,      // 手机号
        EMAIL,      // 邮箱
        ID_CARD,    // 身份证号
        BANK_CARD,  // 银行卡号
        NAME,       // 姓名
        ADDRESS     // 地址
    }
}
